/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2019 Ha Thach (tinyusb.org)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "tusb.h"

#include "usb_descriptors.h"

#include "DAP.h"
#include "DAP_queue.h"

static volatile uint8_t  USB_ResponseIdle;;
static DAP_queue DAP_Cmd_queue;

enum {
    USBD_HID_REQ_EP_CTRL = 0,             /* Request from control endpoint      */
    USBD_HID_REQ_EP_INT,                  /* Request from interrupt endpoint    */
    USBD_HID_REQ_PERIOD_UPDATE            /* Request from periodic update       */
};

//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+

void hid_task(void);

/*处理读取中断*/

u8 uart_recv_buf[64*2];
int uart_send_pos = 0;
int uart_recv_pos = 0;


 void USART1_IRQHandler(){
   u8 v = USART1->dt;
   uart_recv_buf[uart_recv_pos++] = v;
   uart_recv_pos &= 64*2-1;
 }

int old_tick = 0;
int rx_counter = 0;
int cdc_initd = 0;
/*------------- MAIN -------------*/
int hid_main(void)
{

  // init device stack on configured roothub port
	cdc_initd = 0;
  tud_init(0);
  USB_ResponseIdle = 1;
  DAP_queue_init(&DAP_Cmd_queue);

  SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
  SysTick->LOAD = 0xffffffff;

  //接收数据中断
  uart_print_init(115200,0);
  nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
  nvic_irq_enable(USART1_IRQn, 0, 0);
  usart_interrupt_enable(USART1,USART_RDBF_INT,1);

  while (1)
  {
    tud_task(); // tinyusb device task

    hid_task();

    if(cdc_initd){
    	rx_counter++;
      #if 0
      int cur_tick = SysTick->VAL;
      if(old_tick+100>cur_tick){
        char buf[0x10];
        buf[sprintf(buf,"%d\r\n",cur_tick)] = 0;
        tud_cdc_n_write_str(0,buf);
        tud_cdc_n_write_str(0,"12345\r\n");
        tud_cdc_n_write_flush(0);
      }
      #else
      //处理接收到的缓存
      while(1){
        if(uart_recv_pos>uart_send_pos){
          /*发送部分数据*/
          int len = uart_recv_pos - uart_send_pos;
          tud_cdc_n_write(0,uart_recv_buf+uart_send_pos,len);
          uart_send_pos += len;
        }
        else if(uart_recv_pos<uart_send_pos){
          //溢出数据,把尾数据发送完
          tud_cdc_n_write(0,uart_recv_buf+uart_send_pos,64*2-uart_send_pos);
          uart_send_pos = 0;
          continue;
        }
        break;
      }
      tud_cdc_n_write_flush(0);
      #endif
    }
  }
}

//////////////////////////////////////////////////////////////////////////////////
//处理接收到新数据
int _uart_write(int ch);
u8 uart_buf[64];
void tud_cdc_rx_cb(uint8_t itf){
	cdc_initd = 1;
  int len = tud_cdc_n_read(itf,uart_buf,sizeof(uart_buf));
  u8 *ptr = uart_buf;
  while(len>0){
    //发送数据到目标
    _uart_write(*ptr);
    ptr++;
    len--;
  }
}

//设置比特率
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding){
  uart_print_init(p_line_coding->bit_rate,p_line_coding->stop_bits);
}

//--------------------------------------------------------------------+
// Device callbacks
//--------------------------------------------------------------------+

// Invoked when device is mounted
void tud_mount_cb(void)
{
  printf("mount cb:!\n");
}

// Invoked when device is unmounted
void tud_umount_cb(void)
{
  printf("umount cb:!\n");
}

// Invoked when usb bus is suspended
// remote_wakeup_en : if host allow us  to perform remote wakeup
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
void tud_suspend_cb(bool remote_wakeup_en)
{
  (void) remote_wakeup_en;
  printf("tud_suspend_cb:!\n");
}

// Invoked when usb bus is resumed
void tud_resume_cb(void)
{
  printf("tud_resume_cb:!\n");
}

//--------------------------------------------------------------------+
// USB HID
//--------------------------------------------------------------------+

static void send_hid_report(uint8_t report_id, uint32_t btn)
{
  // skip if hid is not ready yet
  if ( !tud_hid_ready() ) return;

  switch(report_id)
  {
    case REPORT_ID_KEYBOARD:
    {
      // use to avoid send multiple consecutive zero report for keyboard
      static bool has_keyboard_key = false;

      if ( btn )
      {
        uint8_t keycode[6] = { 0 };
        keycode[0] = HID_KEY_A;

        //tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, keycode);
        has_keyboard_key = true;
      }else
      {
        // send empty key report if previously has key pressed
        if (has_keyboard_key) tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, NULL);
        has_keyboard_key = false;
      }
    }
    break;

    case REPORT_ID_MOUSE:
    {
      int8_t const delta = 5;

      // no button, right + down, no scroll, no pan
      tud_hid_mouse_report(REPORT_ID_MOUSE, 0x00, delta, delta, 0, 0);
    }
    break;

    case REPORT_ID_CONSUMER_CONTROL:
    {
      // use to avoid send multiple consecutive zero report
      static bool has_consumer_key = false;

      if ( btn )
      {
        // volume down
        uint16_t volume_down = HID_USAGE_CONSUMER_VOLUME_DECREMENT;
        tud_hid_report(REPORT_ID_CONSUMER_CONTROL, &volume_down, 2);
        has_consumer_key = true;
      }else
      {
        // send empty key report (release key) if previously has key pressed
        uint16_t empty_key = 0;
        if (has_consumer_key) tud_hid_report(REPORT_ID_CONSUMER_CONTROL, &empty_key, 2);
        has_consumer_key = false;
      }
    }
    break;

    case REPORT_ID_GAMEPAD:
    {
      // use to avoid send multiple consecutive zero report for keyboard
      static bool has_gamepad_key = false;

      hid_gamepad_report_t report =
      {
        .x   = 0, .y = 0, .z = 0, .rz = 0, .rx = 0, .ry = 0,
        .hat = 0, .buttons = 0
      };

      if ( btn )
      {
        report.hat = GAMEPAD_HAT_UP;
        report.buttons = GAMEPAD_BUTTON_A;
        tud_hid_report(REPORT_ID_GAMEPAD, &report, sizeof(report));

        has_gamepad_key = true;
      }else
      {
        report.hat = GAMEPAD_HAT_CENTERED;
        report.buttons = 0;
        if (has_gamepad_key) tud_hid_report(REPORT_ID_GAMEPAD, &report, sizeof(report));
        has_gamepad_key = false;
      }
    }
    break;

    default: break;
  }
}

// Every 10ms, we will sent 1 report for each HID profile (keyboard, mouse etc ..)
// tud_hid_report_complete_cb() is used to send the next report after previous one is complete
void hid_task(void)
{
  // Poll every 10ms
  const uint32_t interval_ms = 10;
  static uint32_t start_ms = 0;

  start_ms += interval_ms;

  uint32_t const btn = 0;// board_button_read();

  // Remote wakeup
  if ( tud_suspended() && btn )
  {
    // Wake up host if we are in suspend mode
    // and REMOTE_WAKEUP feature is enabled by host
    tud_remote_wakeup();
  }else
  {
    // Send the 1st of report chain, the rest will be sent by tud_hid_report_complete_cb()
    //send_hid_report(REPORT_ID_KEYBOARD, btn);
  }
}

static uint16_t hid_get_report(u8 report_type,u8 rid,u8*buffer,u8 req);
// Invoked when sent REPORT successfully to host
// Application can use this to send the next report
// Note: For composite reports, report[0] is report ID
void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_t len)
{
  (void) instance;
  (void) len;

  uint8_t next_report_id = report[0] + 1u;

  if (next_report_id < REPORT_ID_COUNT)
  {

  }

  if(!hid_send_packet()){
    //发送数据完成
    USB_ResponseIdle = 1;
  }
}


static uint16_t hid_get_report(u8 report_type,u8 rid,u8*buffer,u8 req){
    uint8_t * sbuf;
    int slen;

    switch (report_type)
    {
    case HID_REPORT_TYPE_INPUT:{
      //REQ==CTRL,INT
      if(req==USBD_HID_REQ_EP_CTRL||req==USBD_HID_REQ_EP_INT){
        if(DAP_queue_get_send_buf(&DAP_Cmd_queue,&sbuf,&slen)){
          if(slen>64){
            TU_ASSERT(0);
          }
          else{
            memcpy(buffer,sbuf,slen);
            return 64;
          }
        }

        else if(req==USBD_HID_REQ_EP_INT){
          //USB_ResponseIdle = 1;
        }
      }
    }
    break;
    default:
      break;
    }
}

// Invoked when received GET_REPORT control request
// Application must fill buffer report's content and return its length.
// Return zero will cause the stack to STALL request
uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen)
{
  // TODO not Implemented
  (void) instance;
  (void) report_id;
  (void) report_type;
  (void) buffer;
  (void) reqlen;

  return hid_get_report(report_type,report_id,buffer,USBD_HID_REQ_EP_CTRL);
}

int hid_send_packet()
{
    uint8_t * sbuf;
    int slen;
    if (DAP_queue_get_send_buf(&DAP_Cmd_queue, &sbuf, &slen)) {
        if (slen > 64){
            TU_ASSERT(0);
        }else {
            //usbd_hid_get_report_trigger(0, sbuf, 64);
            tud_hid_report(0,sbuf,64);
        }
    }
    else{
      return 0;
    }
    return 1;
}

// Invoked when received SET_REPORT control request or
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize)
{
  (void) instance;

  uint8_t *rbuf;
  switch(report_type){
    case HID_REPORT_TYPE_INVALID:{
      if(bufsize==0) break;
      if(buffer[0]==ID_DAP_TransferAbort){
        DAP_TransferAbort = 1;
        break;
      }

      if(DAP_queue_execute_buf(&DAP_Cmd_queue,buffer,bufsize,&rbuf)){
        //处理LED灯
        if(USB_ResponseIdle){
          hid_send_packet();
          //处理发送数据
          USB_ResponseIdle = 0;
        }
      }
      else{
        //TU_ASSERT(0);
      }


    }break;
    default: break;
  }
}

/////////////////////////////////////////////////////////////////////////////////////
//bulk  处理
static u8 bulk_buf[64];
static int bulk_recv_len = 0;
static u8  bulk_send_buf[64*8];
static int bulk_send_write = 0;
static int bulk_send_pos = 0;
static void dap_send_buf(uint8_t itf){
  u8 *sbuf = 0;
  int slen = 0;
  if(DAP_queue_get_send_buf(&DAP_Cmd_queue,&sbuf,&slen)){
    int send_len = tud_vendor_n_write(itf,sbuf,slen);
    tud_vendor_n_flush(itf);
    // if(send_len<slen){
    //   int num = slen-send_len;
    //   memcpy(bulk_send_buf+bulk_send_write ,sbuf+send_len,num);
    //   bulk_send_write += num;
    // }
    // else{
    //   bulk_send_write = 0;
    //   bulk_send_pos = 0;
    // }
  }
  else{
    USB_ResponseIdle = 1;
  }
}

void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes){
  dap_send_buf(itf);
}

void tud_vendor_rx_cb(uint8_t itf){
  u8 *rbuf;
  int bytes = tud_vendor_n_read(itf,bulk_buf,DAP_PACKET_SIZE);
  bulk_recv_len += bytes;

  if(
    bulk_recv_len>=DAP_PACKET_SIZE ||
    bytes < 64
  ){
    //刚刚满64字节
    if(DAP_queue_execute_buf(&DAP_Cmd_queue,bulk_buf,bulk_recv_len,&rbuf)){
      if(USB_ResponseIdle){
        dap_send_buf(itf);
        USB_ResponseIdle = 0;
      }
    }
    bulk_recv_len = 0;
  }
}
